# -*-coding:utf-8-*-

from bson.objectid import ObjectId
from daos.base_async import BaseDao, BaseRecord
from utils.misc import generate_random_no
import pymongo
import time
from tornado.gen import coroutine, Return

Users_Document_Structure = {
    '_id': 'USER_ID',
    'user_no': 'USER_NO',  # 用户号，12数字，全局，跨应用，代替手机号使用场合，唯一索引(数字)
    'username': 'USERNAME',
    'password': 'PASSWORD',

    'roles': [],

    'weixin': {
        'mp': {
            'openid': 'OPENID',
        }
    },

    'phone': 'PHONE',
    'email': 'EMAIL',

    'nickname': 'NICKNAME',
    'avatar': 'URL',  # 头像，化身
    'name': 'NAME',

    'addresses': [
        {
            'address_id': 'AID',
            'consignee': '张三',  # 收货人
            'phone': '13000000000',  # 收货人联系电话
            'region': '广东省 珠海市 香洲区',
            'detail': '详细的街道地址',
            'postal_code': '519000',  # 邮政编码
        },
    ],
}


class User(BaseRecord):
    def __getattr__(self, item):
        if 'roles' == item:
            roles = self._get_optional_field('roles')
            if roles is None:
                return []
            return roles
        elif 'user_no' == item:
            return str(self.data['user_no'])
        elif 'app_id' == item:
            return str(self.data['app_id'])
        elif 'wx_mp_openid' == item:
            args = ('weixin', 'mp', 'openid')
        elif 'specifics_vid' == item:
            args = ('specifics', 'vid')
        elif 'specifics_vendor_id' == item:
            args = ('specifics', 'vendor_id')
        else:
            args = (item,)
        return self._get_optional_field(*args)


# NOTE: 需要手动创建索引字段然后再创建索引（对于后建索引的情况）
def _init_collection(collection):
    for a in collection.find({'user_no': {'$exists': 0}}):
        while True:
            try:
                user_no = generate_random_no(12)
                collection.update_one({'_id': a['_id']}, {'$set': {'user_no': user_no}})
                break
            except Exception as e:
                import logging
                logger = logging.getLogger()
                logger.info('user_no: %d is dup, err msg: %s' % (user_no, str(e)))
    collection.create_index([('user_no', pymongo.ASCENDING)], unique=True)


class UsersDao(BaseDao):
    def __init__(self, db):
        super(UsersDao, self).__init__(db, 'users')
        self.collection.create_index([('user_no', pymongo.ASCENDING)], unique=True)

        self.member_fields = ['name', 'nickname', 'phone', 'avatar', 'email', 'user_no', 'spread_bonus_points',
                              'city', 'district', 'roles']
        self.common_fields = ['name', 'nickname', 'phone', 'avatar', 'email', 'user_no']

    @coroutine
    def get_user_raw(self, app, **kwargs):
        filter_cond = {}
        if 'wx_mp_openid' in kwargs:
            wx_mp_openid = kwargs['wx_mp_openid']
            filter_cond = {'app_id': app.id_str, 'weixin.mp.openid': wx_mp_openid}
        elif 'user_id' in kwargs:
            filter_cond = {'_id': ObjectId(kwargs['user_id'])}
        elif 'phone_no' in kwargs:
            filter_cond = {'app_id': app.id_str,
                           '$or': [{'username': kwargs['phone_no']}, {'phone': kwargs['phone_no']}]}
        elif 'user_no' in kwargs:
            filter_cond = {'user_no': kwargs['user_no']}
        elif 'filter_cond' in kwargs:
            filter_cond = kwargs['filter_cond']
            filter_cond.update(app_id=app.id_str)
        projection = kwargs['projection'] if 'projection' in kwargs else None
        result = yield self.find_one(filter_cond, projection)
        raise Return(result)

    @coroutine
    def get_user(self, app, **kwargs):
        data = yield self.get_user_raw(app, **kwargs)
        if data is not None:
            raise Return(User(data))
        else:
            raise Return(None)

    @coroutine
    def get_user_by_wx_mp_openid(self, app, wx_mp_openid):
        result = yield self.get_user(app, wx_mp_openid=wx_mp_openid)
        raise Return(result)

    @coroutine
    def get_user_by_id(self, app, user_id):
        result = yield self.get_user(app, user_id=ObjectId(user_id))
        raise Return(result)

    @coroutine
    def get_user_by_phone_no(self, app, phone_no):
        result = yield self.get_user(app, phone_no=phone_no)
        raise Return(result)

    @coroutine
    def get_user_by_user_no(self, app, user_no):
        result = yield self.get_user(app, user_no=user_no)
        raise Return(result)

    @coroutine
    def add_user(self, app, data):
        phone_no = data['username']
        user = yield self.get_user_by_phone_no(app, phone_no)
        if user is not None:
            result = (False, {'user_id': user.id_str, 'user_no': user.user_no})
            raise Return(result)
        fields = self.extract_fields(data,
                                     required_fields=['username'],
                                     optional_fields=['weixin', 'password', 'avatar', 'nickname', 'name',
                                                      '_id', 'roles', 'superior_id', 'specifics', 'tags', 'alipay'])
        if 'weixin' in fields:
            weixin = fields['weixin']
            if 'nickname' in weixin and weixin['nickname'] is not None:
                fields['nickname'] = weixin['nickname']
            if 'avatar' in weixin and weixin['avatar'] is not None:
                fields['avatar'] = weixin['avatar']
        elif 'alipay' in fields:
            # TODO: support alipay user/consumer
            pass    
        fields['app_id'] = app.id_str
        fields['phone'] = phone_no
        user_id_no = yield self.insert_one_with_unique_no(fields, 'user', 12)
        result = True, user_id_no
        raise Return(result)

    def update_user_with_dealer_id_vendor_id(self, app, dealer_id, vendor_id):
        filter_cond = {'_id': ObjectId(dealer_id)}
        modification = {'dealer_id': dealer_id, 'vendor_id': vendor_id}
        self.collection.find_one_and_update(filter_cond, {'$set': modification})

    def update_user_with_vendor_id(self, app, vendor_id):
        filter_cond = {'_id': ObjectId(vendor_id)}
        modification = {'vendor_id': vendor_id}
        self.collection.find_one_and_update(filter_cond, {'$set': modification})

    @coroutine
    def update_user(self, uid, data):
        data = self.extract_fields(data, optional_fields=['specifics', 'weixin', 'avatar', 'nickname'])
        filter_cond = {'_id': ObjectId(uid)}
        result = yield self.update_one(filter_cond, {'$set': data})
        raise Return(1 == result.matched_count)

    @coroutine
    def delete_user_by_username(self, app, username):
        filter_cond = {
            '$or': [
                {'username': username},
                {'phone': username},
            ],
            'app_id': app.id_str,
        }
        result = yield self.delete_one(filter_cond)
        raise Return(1 == result.deleted_count)

    @coroutine
    def get_user_member_list(self, uid, **kwargs):
        aspect = kwargs['aspect'] if 'aspect' in kwargs else None
        if 'spread' == aspect:
            level = int(kwargs['level']) if 'level' in kwargs else 0
            if 0 == level:
                filter_cond = {'$or': [{'spreader_id': uid}, {'superior_spreader_id': uid}, {'top_spreader_id': uid}]}
            elif 1 == level:
                filter_cond = {'spreader_id': uid}
            elif 2 == level:
                filter_cond = {'superior_spreader_id': uid}
            elif 3 == level:
                filter_cond = {'top_spreader_id': uid}
        elif 'agent' == aspect:
            filter_cond = {}
            if 'city' in kwargs:
                filter_cond.update(city=kwargs['city'])
            if 'district' in kwargs:
                filter_cond.update(district=kwargs['district'])
            if 'roles' in kwargs:
                filter_cond.update(roles={'$in': kwargs['roles']})
            # for vendor case, see PlatformUserHandler
            if 'superior_id' in kwargs:
                filter_cond.update(superior_id=kwargs['superior_id'])
        else:
            filter_cond = {'superior_id': uid}

        # common
        if 'app_id' in kwargs:
            filter_cond.update(app_id=kwargs['app_id'])
        if 'like' in kwargs:
            like = kwargs['like']
            filter_cond.update({'$or': [{'username': {'$regex': like}}, {'phone': {'$regex': like}}]})

        self.logger.info(str(filter_cond))
        if 'get_count' in kwargs and kwargs['get_count']:
            count = yield self.count(filter_cond)
            result = dict(count=count)
        else:
            result = yield self.get_paging_result(filter_cond, self.member_fields, 'user_id', None, **kwargs)
        raise Return(result)

    @coroutine
    def get_user_role_list(self, uid):
        filter_cond = {'_id': ObjectId(uid)}
        projection = {'roles': 1, '_id': 0}
        result = yield self.find_one(filter_cond, projection)
        if result:
            result = result['roles']
        else:
            result = []
        raise Return(result)

    @coroutine
    def create_user_member(self, app, uid, data):
        data.update(superior_id=uid)
        result, user = yield self.add_user(app, data)
        if not result:
            mid = user['user_id']
            # TODO: tmp approach
            data.update(tmp_root_key='5a6acaa5d823efe0644936e5')
            yield self.update_user_member(uid, mid, data)
        raise Return(user)

    @coroutine
    def update_user_member(self, uid, mid, data):
        # TODO: tmp approach
        tmp_root_key = False
        if 'tmp_root_key' in data and '5a6acaa5d823efe0644936e5' == data.pop('tmp_root_key'):
            tmp_root_key = True
        # TODO: tmp approach
        import settings
        if uid == settings.root_user_id or tmp_root_key:
            filter_cond = {'_id': ObjectId(mid)}
        else:
            filter_cond = {'superior_id': uid, '_id': ObjectId(mid)}
        result = yield self.update_one(filter_cond, {'$set': data})
        raise Return(1 == result.matched_count)

    @coroutine
    def get_user_member(self, uid, mid):
        filter_cond = {'superior_id': uid, '_id': ObjectId(mid)}
        result = yield self.find_one(filter_cond, self.member_fields)
        result['user_id'] = str(result.pop('_id'))
        raise Return(result)

    @coroutine
    def delete_user_member(self, uid, mid):
        filter_cond = {'superior_id': uid, '_id': ObjectId(mid)}
        # filter_cond = {'_id': ObjectId(mid)}
        result = yield self.delete_one(filter_cond)
        raise Return(1 == result.deleted_count)

    @coroutine
    def get_user_address_list(self, uid):
        filter_cond = {'_id': ObjectId(uid)}
        result = yield self.get_object_field(filter_cond, 'addresses')
        if result is None:
            result = []
        raise Return(result)

    @coroutine
    def add_user_address(self, uid, data):
        filter_cond = {'_id': ObjectId(uid)}
        data = self.extract_fields(data, required_fields=['consignee', 'phone', 'region', 'detail', 'postal_code'])
        address_id = str(ObjectId())
        data.update(address_id=address_id)
        result = yield self.add_sub_array_element(filter_cond, 'addresses', data)
        if result:
            result = {'address_id': address_id}
        raise Return(result)

    @coroutine
    def update_user_address(self, uid, aid, data):
        filter_cond = {'_id': ObjectId(uid), 'addresses.address_id': aid}
        data = self.extract_fields(data, optional_fields=['consignee', 'phone', 'region', 'detail', 'postal_code'])
        result = yield self.update_sub_array_element(filter_cond, 'addresses', data)
        raise Return(result)

    @coroutine
    def get_user_address(self, uid, aid):
        filter_cond = {'_id': ObjectId(uid), 'addresses.address_id': aid}
        result = yield self.get_sub_array_element(filter_cond, 'addresses')
        raise Return(result)

    @coroutine
    def delete_user_address(self, uid, aid):
        data = {'address_id': aid}
        filter_cond = {'_id': ObjectId(uid)}
        result = yield self.delete_sub_array_element(filter_cond, 'addresses', data)
        raise Return(result)
